﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace BusinessFramework
{
    class EntityBaseRecursiveStrategy<T> : RecursiveStrategy
    {
        public EntityBaseRecursiveStrategy()
        {
            var ex = typeof(IEntityBase).GetProperty("Extensions"); // TODO: optimize

            foreach (var p in ReflectionHelper.GetProperties<T>()
                .Where(p => !p.IsDefined(typeof(IgnoreTraversalAttribute), true)))
            {
                if ((typeof(IEntityBase).IsAssignableFrom(p.PropertyType)))
                {
                    var propertySelector = PrecompilationHelper
                        .GetPropertySelector<T, IEntityExtensions>(p, ex);
                    _properties.Add(propertySelector);
                }
                var paramType = p.PropertyType.GetIEnumerableGenericParam();

                if (paramType != null)
                {
                    if ((typeof(IEntityBase).IsAssignableFrom(paramType)))
                    {
                        Func<T, IEnumerable> collectionSelector = PrecompilationHelper.GetPropertySelector<T, IEnumerable>(p);
                        Func<object, IEntityExtensions> selector = PrecompilationHelper.GetPropertySelector<object, IEntityExtensions>(ex);

                        var pp = new Pair<Func<T, IEnumerable>, Func<object, IEntityExtensions>>(collectionSelector, selector);
                        _collections.Add(pp);
                    }
                }
            }
        }

        private readonly List<Func<T, IEntityExtensions>> _properties = new List<Func<T, IEntityExtensions>>();
        private readonly List<Pair<Func<T, IEnumerable>, Func<object, IEntityExtensions>>> _collections = new List<Pair<Func<T, IEnumerable>, Func<object, IEntityExtensions>>>();

        protected override IEnumerable<IEntityExtensions> GetSimpleProperties(object target)
        {
            foreach (var property in _properties)
            {
                var r = property((T) target);
                if (r != null)
                    yield return r;
            }
        }

        public override IEnumerable<IEnumerable> GetCollections(object target)
        {
            foreach (var collectionPair in _collections)
            {
                var collectionSelector = collectionPair.One;
                var r = collectionSelector((T) target);

                if ( r != null)
                    yield return r;
            }
        }

        protected override IEnumerable<IEntityExtensions> GetCollectionsElements(object target)
        {
            foreach (var collectionPair in _collections)
            {
                var collectionSelector = collectionPair.One;
                var elementSelector = collectionPair.Two;

                var collection = collectionSelector((T)target);
                if ( collection == null) continue;

                foreach (var element in collection)
                {
                    var r = elementSelector(element);
                    if (r != null)
                        yield return r;
                }
            }
        }
    }
}